package com.google.code.instrumentation.logging;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

import org.slf4j.Logger;

import com.google.code.instrumentation.util.CompileTimeClassUtil;

public class Slf4jMethodInstrumentor extends AbstractMethodInstrumentor {

	protected Slf4jMethodInstrumentor(final CtMethod method) {
		super(method);
	}

	@Override
	public void addLogEnd() throws CannotCompileException {
		final String methodName = ctMethod.getName();
		final boolean asFinally = true;

		try {
			ctMethod.addLocalVariable("elapsedTime",
					CompileTimeClassUtil.getCtClassFor(long.class));
		} catch (final NotFoundException e) {
			// Should not happen
			throw new RuntimeException("Failed to find JRE class", e);
		}

		// We don't have access to the local variable. So just output the log message twice for now!
		ctMethod.insertAfter("elapsedTime = System.currentTimeMillis() - startTime;");
		ctMethod.insertAfter("log.trace(\"EXIT: [{}] ElapsedTime: {}ms\", new Object[] {methodName,Long.valueOf(elapsedTime)});");

		ctMethod.insertAfter(
				String.format(
						"org.slf4j.LoggerFactory.getLogger(this.getClass()).trace(\"EXIT: [{}]\",new Object[]{\"%s\"});",
						methodName), asFinally);
	}

	@Override
	public void addLogStart() throws CannotCompileException {
		try {
			final CtClass slf4jClass = CompileTimeClassUtil
					.getCtClassFor(Logger.class);
			ctMethod.addLocalVariable("log", slf4jClass);
			final CtClass stringClass = CompileTimeClassUtil
					.getCtClassFor(String.class);
			ctMethod.addLocalVariable("methodName", stringClass);
			final CtClass longClass = CompileTimeClassUtil
					.getCtClassFor(long.class);
			ctMethod.addLocalVariable("startTime", longClass);

			final String methodName = ctMethod.getName();

			// subsequent insert statements mean that the final statement
			// order is in reverse
			ctMethod.insertBefore("log.trace(\"ENTRY: [{}({})]\", new Object[] {methodName, java.util.Arrays.deepToString($args)});");
			ctMethod.insertBefore("log = org.slf4j.LoggerFactory.getLogger(this.getClass());");
			ctMethod.insertBefore("methodName = \"" + methodName + "\";");
			ctMethod.insertBefore("startTime = System.currentTimeMillis();");

		} catch (final NotFoundException e) {
			throw new CannotCompileException(e);
		}
	}

}
